package com.artnology.independence.reengineering;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;

import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.PackageFragment;
import org.eclipse.jface.action.ContributionItem;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;

import com.artnology.independence.views.LogicalOperator;

@SuppressWarnings("restriction")
public class ReengineeringMenuItem extends ContributionItem {

	@Override
	public void fill(Menu menu, int index) {
		// create the menu item
		MenuItem menuItem = new MenuItem(menu, SWT.CHECK, index);
		menuItem.setText("Re-engineer for Independence...");

		IWorkbenchWindow window = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow();

		final ISelection selection = window.getActivePage().getSelection();

		if (!(selection instanceof IStructuredSelection)) {
			menuItem.setEnabled(false);
			return;
		}

		final IStructuredSelection structuredselection = (IStructuredSelection) window
				.getActivePage().getSelection();

		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				ReengineeringInputDialog dlg = new ReengineeringInputDialog();
				if (dlg.open() == Window.OK) {
					// User clicked OK; update the label with the input
					Set<ITypeRoot> allTypesToReengineer = getAllTypesFromSelection(
							structuredselection, dlg.getValue());
					if (allTypesToReengineer.size() > 0) {
						ReengineeringParser rp = new ReengineeringParser();
						rp.execute(allTypesToReengineer);
					} else {
						MessageDialog.openWarning(Display.getCurrent()
								.getActiveShell(), "No types to re-engineer",
								"There are no types that match this pattern '"
										+ dlg.getValue() + "'");
					}
				}

			}
		});
		menuItem.setEnabled(figureEnablementState());

	}

	@Override
	public boolean isDynamic() {
		return true;
	}

	private boolean figureEnablementState() {
		IWorkbenchWindow window = PlatformUI.getWorkbench()
				.getActiveWorkbenchWindow();
		IStructuredSelection selection = (IStructuredSelection) window
				.getActivePage().getSelection();

		if (selection == null) {
			return false;
		}

		try {
			for (Iterator<?> selectionIterator = selection.iterator(); selectionIterator
					.hasNext();) {
				Object nextSelection = selectionIterator.next();
				if (nextSelection instanceof IAdaptable) {
					IAdaptable a = (IAdaptable) nextSelection;
					IJavaElement javaElement = (IJavaElement) a
							.getAdapter(IJavaElement.class);
					if (javaElement == null) {
						// if one of the selection items is not a java element
						// then we shouldn't execute command
						return false;
					}
					// if the selection is a java project then we should check
					// whether it contains a SRC package root as we don't want
					// to automatically reengineer all binary libraries.
					if (javaElement instanceof IJavaProject) {
						IJavaProject theProject = (IJavaProject) javaElement;
						IJavaElement[] allChildren = theProject.getChildren();
						boolean projectHasSrcFolder = false;
						for (int i = 0; i < allChildren.length; i++) {
							if (allChildren[i] instanceof IPackageFragmentRoot
									&& ((IPackageFragmentRoot) allChildren[i])
											.getKind() == IPackageFragmentRoot.K_SOURCE) {
								projectHasSrcFolder = true;
							}
						}
						if (!projectHasSrcFolder) {
							return false;
						}
					}

				} else {
					return false;
				}
			}
		} catch (JavaModelException e) {
			e.printStackTrace();
		}
		// now figure whether we find real java types to parse
		return true;

	}

	private static final Set<ITypeRoot> getAllTypesFromSelection(
			IStructuredSelection selection, String typeNamePattern) {

		Set<ITypeRoot> typeTreeFlattened = new HashSet<ITypeRoot>();

		for (Iterator<?> selectionIterator = selection.iterator(); selectionIterator
				.hasNext();) {
			Object nextSel = selectionIterator.next();

			IAdaptable a = (IAdaptable) nextSel;
			IJavaElement nextSelection = (IJavaElement) a
					.getAdapter(IJavaElement.class);
			try {
				if (nextSelection instanceof IPackageFragment) {
					IPackageFragment mypackage = (IPackageFragment) nextSelection;
					// put new root into type tree
					for (int i = 0; i < mypackage.getChildren().length; i++) {
						typeTreeFlattened.add((ITypeRoot) mypackage
								.getChildren()[i]);
					}
					for (IJavaElement subPackage : getSubPackages(mypackage)) {
						IPackageFragment subPack = (IPackageFragment) subPackage;
						for (int y = 0; y < subPack.getChildren().length; y++) {
							typeTreeFlattened.add((ITypeRoot) subPack
									.getChildren()[y]);
						}
					}
				} else if (nextSelection instanceof ITypeRoot) {
					typeTreeFlattened.add((ITypeRoot) nextSelection);
				} else if (nextSelection instanceof IPackageFragmentRoot) {
					IPackageFragmentRoot packRoot = (IPackageFragmentRoot) nextSelection;
					populateTypeTreeFromPackageRoot(typeTreeFlattened, packRoot);

				} else if (nextSelection instanceof IJavaProject) {
					IJavaProject theProject = (IJavaProject) nextSelection;
					IJavaElement[] allChildren = theProject.getChildren();
					for (int i = 0; i < allChildren.length; i++) {
						if (allChildren[i] instanceof IPackageFragmentRoot
								&& ((IPackageFragmentRoot) allChildren[i])
										.getKind() == IPackageFragmentRoot.K_SOURCE) {
							populateTypeTreeFromPackageRoot(typeTreeFlattened,
									(IPackageFragmentRoot) allChildren[i]);
						}
					}
				}
			} catch (JavaModelException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		if (!typeNamePattern.equals("*")) {
			
			DVNodePattern pattern = new DVNodePattern(typeNamePattern,
					LogicalOperator.AND);

			Set<ITypeRoot> typeTreeFlattenedFiltered = new HashSet<ITypeRoot>();

			for (ITypeRoot iTypeRoot : typeTreeFlattened) {
				Matcher matcher = pattern.getTypePattern().matcher(
						iTypeRoot.findPrimaryType().getFullyQualifiedName()
								.toLowerCase());
				if (matcher.find()) {
				
					typeTreeFlattenedFiltered.add(iTypeRoot);
				}
			}
			return typeTreeFlattenedFiltered;
		} else {
			return typeTreeFlattened;
		}

	}

	private static final List<IJavaElement> getSubPackages(IPackageFragment p)
			throws Exception {
		List<IJavaElement> result = new ArrayList<IJavaElement>();
		IJavaElement[] packages = ((IPackageFragmentRoot) p.getParent())
				.getChildren();
		String[] names = ((PackageFragment) p).names;
		int namesLength = names.length;
		newPackage: for (int i = 0, length = packages.length; i < length; i++) {
			String[] otherNames = ((PackageFragment) packages[i]).names;
			if (otherNames.length <= namesLength)
				continue;

			for (int j = 0; j < namesLength; j++) {
				if (!names[j].equals(otherNames[j]))
					continue newPackage;
			}
			result.add(packages[i]);
		}

		return result;
	}

	private static final void populateTypeTreeFromPackageRoot(
			Set<ITypeRoot> typeTreeFlattened, IPackageFragmentRoot packRoot)
			throws JavaModelException, Exception {
		for (int i = 0; i < packRoot.getChildren().length; i++) {
			for (IJavaElement subPackage : getSubPackages((IPackageFragment) packRoot
					.getChildren()[i])) {
				IPackageFragment subPack = (IPackageFragment) subPackage;
				for (int y = 0; y < subPack.getChildren().length; y++) {
					typeTreeFlattened.add((ITypeRoot) subPack.getChildren()[y]);
				}
			}
		}
	}

}
